package ru.usu.gv.gui.analysis;

import ru.usu.gv.core.GraphCanvas;
import ru.usu.gv.graph.Graph;
import ru.usu.gv.graph.Vertex;
import ru.usu.gv.uicomponents.UIEdge;
import ru.usu.gv.uicomponents.UIGraph;

import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import java.util.List;

/**
 @author Sergey Pupyrev 
 13.04.2007
 */

public class CrossingAnalyzer extends GraphAnalyzer
{
	public CrossingAnalyzer()
	{
		super(AnalysisPanel.getInstance().getCrossingSlider());
	}

	@Override
	protected double calculateAnalysValue()
	{
		UIGraph uiGraph = GraphCanvas.getInstance().getCurrentGraph();
		Graph graph = uiGraph.getGraph();

		double m = graph.getEdgeSize();
		double cmx = (m > 0 ? m * (m - 1) / 2.0 : 0);
		for (int i = 0; i < graph.getSize(); i++)
		{
			Vertex v = graph.getVertex(i);
			double degree = v.getEdgesSize();
			cmx -= 0.5 * degree * (degree - 1.0);
		}

		double crossX = 0, crossY = 0, crossZ = 0;
		List<UIEdge> edges = uiGraph.getEdges();
		for (int i = 0; i < edges.size(); i++)
			for (int j = i + 1; j < edges.size(); j++)
			{
				UIEdge edge1 = edges.get(i);
				UIEdge edge2 = edges.get(j);

				Point3d v1 = edge1.getV1().getPosition();
				Point3d v2 = edge1.getV2().getPosition();
				Point3d u1 = edge2.getV1().getPosition();
				Point3d u2 = edge2.getV2().getPosition();

				Vector3d v = new Vector3d(v2);
				v.sub(v1);

				Vector3d u = new Vector3d(u2);
				u.sub(u1);

				if (isCrossX(new Point3d(v1), new Point3d(v2), new Point3d(u1), new Point3d(u2)))
					crossX++;
				if (isCrossY(new Point3d(v1), new Point3d(v2), new Point3d(u1), new Point3d(u2)))
					crossY++;
				if (isCrossZ(new Point3d(v1), new Point3d(v2), new Point3d(u1), new Point3d(u2)))
					crossZ++;
			}

		//System.out.println("crossX = " + crossX);
		//System.out.println("crossY = " + crossY);
		//System.out.println("crossZ = " + crossZ);
		//System.out.println("cmx = " + cmx);

		double nx = 1 - (cmx > 0 ? crossX / cmx : 0);
		double ny = 1 - (cmx > 0 ? crossY / cmx : 0);
		double nz = 1 - (cmx > 0 ? crossZ / cmx : 0);
		return (nx + ny + nz) / 3.0;
	}

	private boolean isCrossX(Point3d p1, Point3d p2, Point3d p3, Point3d p4)
	{
		return isIntersect(new Point2d(p1.y, p1.z), new Point2d(p2.y, p2.z), new Point2d(p3.y, p3.z), new Point2d(p4.y, p4.z));
	}

	private boolean isCrossY(Point3d p1, Point3d p2, Point3d p3, Point3d p4)
	{
		return isIntersect(new Point2d(p1.x, p1.z), new Point2d(p2.x, p2.z), new Point2d(p3.x, p3.z), new Point2d(p4.x, p4.z));
	}

	private boolean isCrossZ(Point3d p1, Point3d p2, Point3d p3, Point3d p4)
	{
		return isIntersect(new Point2d(p1.x, p1.y), new Point2d(p2.x, p2.y), new Point2d(p3.x, p3.y), new Point2d(p4.x, p4.y));
	}

	private boolean isIntersect(Point2d p1, Point2d p2, Point2d p3, Point2d p4)
	{
		if (p1.x > p2.x)
			return isIntersect(p2, p1, p3, p4);
		if (p3.x > p4.x)
			return isIntersect(p1, p2, p4, p3);

		if (Math.max(p1.x, p2.x) <= Math.min(p3.x, p4.x))
			return false;
		if (Math.max(p3.x, p4.x) <= Math.min(p1.x, p2.x))
			return false;

		if (Math.max(p1.y, p2.y) <= Math.min(p3.y, p4.y))
			return false;
		if (Math.max(p3.y, p4.y) <= Math.min(p1.y, p2.y))
			return false;

		Point2d p31 = new Point2d(p3);
		p31.sub(p1);
		Point2d p21 = new Point2d(p2);
		p21.sub(p1);
		Point2d p41 = new Point2d(p4);
		p41.sub(p1);

		if ((p31.x * p21.y - p31.y * p21.x) * (p41.x * p21.y - p41.y * p21.x) >= 0)
			return false;

		Point2d p13 = new Point2d(p1);
		p13.sub(p3);
		Point2d p43 = new Point2d(p4);
		p43.sub(p3);
		Point2d p23 = new Point2d(p2);
		p23.sub(p3);

		if ((p13.x * p43.y - p13.y * p43.x) * (p23.x * p43.y - p23.y * p43.x) >= 0)
			return false;

		return true;
	}

}
